package com.enjoyor.soa.traffic.frame.interceptor;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;


import com.enjoyor.soa.traffic.frame.support.mybatis.page.Dialect;
import com.enjoyor.soa.traffic.frame.support.mybatis.page.MySQLDialect;
import com.enjoyor.soa.traffic.frame.support.mybatis.page.OracleDialect;

@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
public class PageInterceptor implements Interceptor {

    private String databaseType;

    @SuppressWarnings("rawtypes")
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(statementHandler,
                SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                new DefaultReflectorFactory());

        RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
        if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
            return invocation.proceed();
        }

        DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler
                .getValue("delegate.parameterHandler");

        Map map = (Map) defaultParameterHandler.getParameterObject();

        String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");

        if (map != null) {
            Object sidx = map.get("_sidx");
            Object sord = map.get("_sord");

            if (sidx != null && sord != null) {
                if (originalSql.contains("order")) {
                    originalSql = originalSql.substring(0, originalSql.indexOf("order"));
                }
                originalSql = originalSql + " order by " + sidx + " " + sord;
            }
        }

        Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");

        if (databaseType == null) {
            throw new RuntimeException("the value of the dialect property in configuration.xml is not defined : "
                    + configuration.getVariables().getProperty("dialect"));
        }

        Dialect dialect = null;

        switch (databaseType) {
            case "oracl":
                dialect = new OracleDialect();
                break;
            case "mysql":
                dialect=new MySQLDialect();
                break;

        }

        metaStatementHandler.setValue("delegate.boundSql.sql",
                dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()));
        metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        this.databaseType = properties.getProperty("databaseType");
    }

}
